#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
统一的文件上传器接口

支持多种上传方式：
- OBS (华为云对象存储)
- 本地存储
- 其他云存储 (可扩展)
"""

import asyncio
import logging
from abc import ABC, abstractmethod
from pathlib import Path
from typing import Dict, Any, Optional, Union
from dataclasses import dataclass

logger = logging.getLogger(__name__)

@dataclass
class UploadResult:
    """上传结果"""
    success: bool
    file_url: Optional[str] = None
    object_key: Optional[str] = None
    error: Optional[str] = None
    metadata: Optional[Dict[str, Any]] = None

class BaseUploader(ABC):
    """上传器基类"""
    
    @abstractmethod
    async def upload_file(
        self, 
        file_path: Union[str, Path], 
        object_key: Optional[str] = None,
        **kwargs
    ) -> UploadResult:
        """上传文件"""
        pass
    
    @abstractmethod
    async def upload_bytes(
        self, 
        data: bytes, 
        filename: str, 
        **kwargs
    ) -> UploadResult:
        """上传字节数据"""
        pass
    
    @abstractmethod
    def is_available(self) -> bool:
        """检查上传器是否可用"""
        pass

class ObsUploader(BaseUploader):
    """OBS上传器"""
    
    def __init__(self, obs_config: Optional[Dict[str, Any]] = None):
        """
        初始化OBS上传器
        
        Args:
            obs_config: OBS配置信息
        """
        self.obs_config = obs_config
        self.obs_client = None
        self._init_client()
    
    def _init_client(self):
        """初始化OBS客户端"""
        try:
            from obs_uploader import ObsUploader as OriginalObsUploader
            self.obs_client = OriginalObsUploader(self.obs_config)
        except ImportError:
            logger.warning("obs_uploader模块未找到")
            self.obs_client = None
        except Exception as e:
            logger.warning(f"OBS客户端初始化失败: {e}")
            self.obs_client = None
    
    async def upload_file(
        self, 
        file_path: Union[str, Path], 
        object_key: Optional[str] = None,
        **kwargs
    ) -> UploadResult:
        """上传文件到OBS"""
        if not self.is_available():
            return UploadResult(
                success=False,
                error="OBS上传器不可用"
            )
        
        try:
            if self.obs_client is None:
                return UploadResult(
                    success=False,
                    error="OBS客户端未初始化"
                )
            
            result = await self.obs_client.upload_file(file_path, object_key, **kwargs)
            
            return UploadResult(
                success=result.get('success', False),
                file_url=result.get('file_url'),
                object_key=result.get('object_key'),
                error=result.get('error'),
                metadata={'uploader': 'obs'}
            )
        except Exception as e:
            logger.error(f"OBS文件上传失败: {e}")
            return UploadResult(
                success=False,
                error=str(e)
            )
    
    async def upload_bytes(
        self, 
        data: bytes, 
        filename: str, 
        **kwargs
    ) -> UploadResult:
        """上传字节数据到OBS"""
        if not self.is_available():
            return UploadResult(
                success=False,
                error="OBS上传器不可用"
            )
        
        try:
            if self.obs_client is None:
                return UploadResult(
                    success=False,
                    error="OBS客户端未初始化"
                )
            
            result = await self.obs_client.upload_bytes(data, filename, **kwargs)
            
            return UploadResult(
                success=result.get('success', False),
                file_url=result.get('file_url'),
                object_key=result.get('object_key'),
                error=result.get('error'),
                metadata={'uploader': 'obs'}
            )
        except Exception as e:
            logger.error(f"OBS字节上传失败: {e}")
            return UploadResult(
                success=False,
                error=str(e)
            )
    
    def is_available(self) -> bool:
        """检查OBS上传器是否可用"""
        return self.obs_client is not None and self.obs_client.is_available()

class LocalUploader(BaseUploader):
    """本地文件上传器（复制到指定目录）"""
    
    def __init__(self, base_dir: Union[str, Path], base_url: str = ""):
        """
        初始化本地上传器
        
        Args:
            base_dir: 本地存储基础目录
            base_url: 访问文件的基础URL
        """
        self.base_dir = Path(base_dir)
        self.base_url = base_url.rstrip('/')
        
        # 确保目录存在
        self.base_dir.mkdir(parents=True, exist_ok=True)
    
    async def upload_file(
        self, 
        file_path: Union[str, Path], 
        object_key: Optional[str] = None,
        **kwargs
    ) -> UploadResult:
        """复制文件到本地目录"""
        try:
            import shutil
            
            file_path = Path(file_path)
            if not file_path.exists():
                return UploadResult(
                    success=False,
                    error=f"源文件不存在: {file_path}"
                )
            
            # 确定目标路径
            if object_key:
                target_path = self.base_dir / object_key
            else:
                target_path = self.base_dir / file_path.name
            
            # 确保目标目录存在
            target_path.parent.mkdir(parents=True, exist_ok=True)
            
            # 复制文件
            shutil.copy2(file_path, target_path)
            
            # 构建访问URL
            relative_path = target_path.relative_to(self.base_dir)
            file_url = f"{self.base_url}/{relative_path}" if self.base_url else str(target_path)
            
            logger.info(f"文件已复制到本地: {target_path}")
            
            return UploadResult(
                success=True,
                file_url=file_url,
                object_key=str(relative_path),
                metadata={'uploader': 'local', 'local_path': str(target_path)}
            )
            
        except Exception as e:
            logger.error(f"本地文件复制失败: {e}")
            return UploadResult(
                success=False,
                error=str(e)
            )
    
    async def upload_bytes(
        self, 
        data: bytes, 
        filename: str, 
        **kwargs
    ) -> UploadResult:
        """保存字节数据到本地文件"""
        try:
            object_key = kwargs.get('object_key', filename)
            target_path = self.base_dir / object_key
            
            # 确保目标目录存在
            target_path.parent.mkdir(parents=True, exist_ok=True)
            
            # 写入文件
            target_path.write_bytes(data)
            
            # 构建访问URL
            relative_path = target_path.relative_to(self.base_dir)
            file_url = f"{self.base_url}/{relative_path}" if self.base_url else str(target_path)
            
            logger.info(f"字节数据已保存到本地: {target_path}")
            
            return UploadResult(
                success=True,
                file_url=file_url,
                object_key=str(relative_path),
                metadata={'uploader': 'local', 'local_path': str(target_path)}
            )
            
        except Exception as e:
            logger.error(f"本地字节保存失败: {e}")
            return UploadResult(
                success=False,
                error=str(e)
            )
    
    def is_available(self) -> bool:
        """检查本地上传器是否可用"""
        return self.base_dir.exists() and self.base_dir.is_dir()

class UploaderManager:
    """上传器管理器"""
    
    def __init__(self):
        self.uploaders: Dict[str, BaseUploader] = {}
    
    def register_uploader(self, name: str, uploader: BaseUploader):
        """注册上传器"""
        self.uploaders[name] = uploader
        logger.info(f"已注册上传器: {name}")
    
    def get_uploader(self, name: str) -> Optional[BaseUploader]:
        """获取上传器"""
        return self.uploaders.get(name)
    
    def get_available_uploaders(self) -> Dict[str, bool]:
        """获取可用的上传器列表"""
        return {name: uploader.is_available() for name, uploader in self.uploaders.items()}
    
    async def upload_with_fallback(
        self, 
        file_path: Union[str, Path], 
        uploader_names: list,
        object_key: Optional[str] = None,
        **kwargs
    ) -> UploadResult:
        """使用回退机制上传文件"""
        for uploader_name in uploader_names:
            uploader = self.get_uploader(uploader_name)
            if uploader and uploader.is_available():
                try:
                    result = await uploader.upload_file(file_path, object_key, **kwargs)
                    if result.success:
                        logger.info(f"文件上传成功，使用上传器: {uploader_name}")
                        return result
                    else:
                        logger.warning(f"上传器 {uploader_name} 上传失败: {result.error}")
                except Exception as e:
                    logger.warning(f"上传器 {uploader_name} 发生异常: {e}")
                    continue
        
        return UploadResult(
            success=False,
            error="所有上传器都不可用或上传失败"
        )

# 全局上传器管理器实例
uploader_manager = UploaderManager()

# 便捷函数
def create_obs_uploader(obs_config: Optional[Dict[str, Any]] = None) -> ObsUploader:
    """创建OBS上传器"""
    return ObsUploader(obs_config)

def create_local_uploader(base_dir: Union[str, Path], base_url: str = "") -> LocalUploader:
    """创建本地上传器"""
    return LocalUploader(base_dir, base_url)

def register_default_uploaders(
    obs_config: Optional[Dict[str, Any]] = None,
    local_base_dir: Optional[Union[str, Path]] = None,
    local_base_url: str = ""
):
    """注册默认的上传器"""
    # 注册OBS上传器
    if obs_config or True:  # 总是尝试注册，让它自己检测可用性
        obs_uploader = create_obs_uploader(obs_config)
        uploader_manager.register_uploader("obs", obs_uploader)
    
    # 注册本地上传器
    if local_base_dir:
        local_uploader = create_local_uploader(local_base_dir, local_base_url)
        uploader_manager.register_uploader("local", local_uploader) 